package org.mashart.pink.m.gps;

import javax.microedition.location.LocationProvider;
import javax.microedition.location.Criteria;
import javax.microedition.location.LocationListener;
import javax.microedition.location.Location;
import javax.microedition.location.QualifiedCoordinates;

/**
 * Implementation of <code>DeviceConnector</code> for built-in GPS. 
 * Using Location API: JSR 179
 */
public class InternalGpsConnector implements DeviceConnector, LocationListener {

    private LocationProvider provider;
    private QualifiedCoordinates qc;
    private Waypoint evlWp;
    private Waypoint fxdWp;
    private int latestOutputTimeMS;

    /**
     * Configures the connector. For built-in GPS requires, do nothing.
     * @return 1
     */
    public int configure() {
        return 1;
    }

    /**
     * Check if the Location API is available
     */
    public static boolean isAPIAvailable(){
        return System.getProperty("microedition.location.version") != null;
    }

    /**
     * Turns on the built-in GPS device
     * @return 1 if succeeded, otherwise 0
     */
    public int initialize() {
        try {
            // Mobile device with a built-in GPS may also have other positioning
            // mechanisms, e.g., using the WLAN Accss-point or GSM base-station information
            // It is up to user to enable GPS and disable WLAN/base-station positioning
            // here data on: altitude, speed, course over ground are required.
            Criteria c = new Criteria();
            c.setAltitudeRequired(true);
            c.setSpeedAndCourseRequired(true);
            provider = LocationProvider.getInstance(c);
        } catch (Exception e) {
            return 0;
        }
        return provider != null ? 1 : 0;
    }

    /**
     * Start listening the location information from built-in GPS
     * @return 1 if succeeded, -1 if failed
     */
    public int activate() {
        // reset the waypoint
        evlWp = null;
        fxdWp = null;
        latestOutputTimeMS = -1;
        provider.setLocationListener(this, -1, 0, 0);
        return 1;
    }

    /**
     * Stop listening the location information from built-in GPS
     * @return 1 if succeeded, -1 if failed
     */
    public int deactivate() {
        provider.setLocationListener(null, -1, 0, 0);
        return 1;
    }

    /**
     * Get the latest waypoint
     * @return a waypoint if the connector is ready, otherwise return null.
     */
    public Waypoint getWaypoint() {
        // if a waypoint with the same timestamp have been read already
        if (fxdWp.timeMS == latestOutputTimeMS) {
            return null;
        } else {
            latestOutputTimeMS = fxdWp.timeMS;
            return fxdWp;
        }
    }

    /**
     * Turns off the built-in GPS device
     * @return 1 if succeeded, otherwise 0
     */
    public int terminate() {
        deactivate();
        try {
            provider.reset();
            return 1;
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * Implement the interface LocationListener.
     * Called when the location is updated.
     * @param prov
     * @param loc
     */
    public void locationUpdated(final LocationProvider prov, final Location loc) {
        new Thread() {
            public void run() {
                if (loc != null && loc.isValid()) {
                    qc = loc.getQualifiedCoordinates();
                    evlWp = new Waypoint();
                    evlWp.latitude = qc.getLatitude();
//                    evlWp.ns = (evlWp.latitude > 0);
                    evlWp.longitude = qc.getLongitude();
//                    evlWp.ew = (evlWp.longitude > 0);
                    evlWp.altitude = Waypoint.round(qc.getAltitude());
                    evlWp.acch = qc.getHorizontalAccuracy();
                    evlWp.accv = qc.getVerticalAccuracy();
                    float course = loc.getCourse();
                    if(Float.isNaN(course)){
                        evlWp.course = Integer.MAX_VALUE;
                    }else{
                        evlWp.course = Waypoint.round( course );
                    }
                    float spd = loc.getSpeed();
                    if (!Float.isNaN(spd)) {
                        // convert the unit of speed to "km/h"
                        evlWp.speed = (float) (spd * 3.6);
                    }

                    long t = loc.getTimestamp();
                    int d = (int) (t / (24 * 3600000));
                    // calculate the elapsed milliseconds from 00:00:00, important for calculation
                    evlWp.timeMS = (int) (t - 24 * 3600000 * d);
                    evlWp.fix = (prov.getState() == LocationProvider.AVAILABLE);
                }
                fxdWp = evlWp;
                evlWp = null;
            }
        }.start();
    }

    /**
     * Implement the interface LocationListener.
     * Called when the state of location provider is changed.
     * Do nothing here.
     * @param prov
     * @param newState
     */
    public void providerStateChanged(LocationProvider prov, int newState) {
    }
} 